home *** CD-ROM | disk | FTP | other *** search
- /*
- 89/03/02: ERS: added the "mode" argument for MS-DOS/Unix
- compatibility. Added prototypes for GNU C.
-
- fixed spawnve: the original didn't pass the command tail
- correctly, nor did it handle errno right. Also, this version
- passes args in the environment as well as the command line,
- using the ARGV= mechanism supported by crt0.s
-
- Written 89/01/10 by ERS. Original version Copyright (c) 1988 by
- Memorial University of Newfoundland. This version is based upon
- that original, but is substantially different. (In fact, there
- probably isn't a single line of the original left.)
-
- Adapted 90/06/25 by ERS to MiNT.
-
- 08 Apr 93 hohmuth
- added support for ARGV standard extension allowing empty arguments
-
- uo, 3.5.93, throw away some static characterarrays (path[] and
- cmd[]).
-
- 10 Jul 1993 ole
- added emulation for script execution.
- This feature is controlable by the Environment variable "UNIXMODE".
- The switch 's' in UNIXMODE says that executable text files with a
- hash as their first character are interpretable.
- Files which have only '#' as their first character are interpreted
- using /bin/sh
- Files which have i.e '#!/usr/bin/awk -f' as their first character
- are interpreted with "/usr/bin/awk -f <file>"
-
- 17 Sep 1993 schwab
- cleaned up emulation code for script execution.
- File is only interpretable if first two bytes are "#!".
- Don't modify errno if there is no error.
- */
-
- #include <stdarg.h>
- #include <process.h>
- #include <param.h>
- #include <errno.h>
- #include <osbind.h>
- #include <mintbind.h>
- #include <stdlib.h>
- #include <time.h>
- #include <string.h>
- #include <unistd.h>
- #include <support.h>
- #include "lib.h"
-
- #define TOS_ARGS 126
-
- extern char **environ;
- extern int __mint;
-
- /* comment this out, if you don't want script execution */
- #define HASH_BANG
-
- #ifdef HASH_BANG
-
- static char *const extensions[] = { "ttp", "prg", "tos", NULL };
-
- static int interpret_script __PROTO((int mode, const char *path, const char *,
- char *const *argv, char *const *envp));
-
- static int
- interpret_script(mode, path, _path, argv, envp)
- int mode;
- const char *path;
- const char *_path;
- char *const *argv;
- char *const *envp;
- {
- char tmppath[MAXPATHLEN];
- char *shell;
- char *shellargs;
- char **shellargv;
- int nargcount;
- int fd;
- int argcount;
- int i, rv;
- char buf[1024], *bufp, *argp;
- long r;
-
- /* path is already converted to dos */
- if ((fd = (int)Fopen(path, 0)) < 0)
- {
- errno = -fd;
- return -1;
- }
-
- r = Fread (fd, (long) sizeof (buf) - 1L, buf);
- Fclose (fd);
- if (r < 0)
- {
- errno = -(int) r;
- return -1;
- }
- buf[r] = 0;
-
- if (buf[0] == '#' && buf[1] == '!')
- {
- /* skip blanks */
- bufp = buf + 2;
- while (*bufp == '\t' || *bufp == ' ')
- bufp++;
-
- /* read filename */
- shell = bufp;
- while (*bufp && *bufp != ' ' && *bufp != '\t'
- && *bufp != '\r' && *bufp != '\n')
- bufp++;
-
- if (*bufp && *bufp != '\r' && *bufp != '\n')
- *bufp++ = 0;
- /* else the null will be added below */
-
- /*
- * read arguments if any
- */
- argp = shellargs = bufp;
- nargcount = 0;
- i = 0;
- while (*bufp && *bufp != '\r' && *bufp != '\n')
- {
- /* skip blanks */
- while (*bufp == ' ' || *bufp == '\t')
- bufp++;
-
- if (*bufp == '\r' || *bufp == '\n')
- break;
-
- while (*bufp && *bufp != ' ' && *bufp != '\t'
- && *bufp != '\r' && *bufp != '\n')
- *argp++ = *bufp++;
- *argp++ = '\0';
- nargcount++;
- }
- *argp = '\0';
-
- if (*shell)
- {
- shell = _buffindfile (shell, getenv("PATH"), extensions, tmppath);
- if (!shell)
- {
- errno = ENOENT;
- return -1; /* file not found */
- }
-
- /* count old args */
- for (i = 0; argv[i] != 0; i++);
- argcount = i;
-
- shellargv = (char **)Malloc((argcount + nargcount + 2) * sizeof(char *));
- if (!shellargv) {
- errno = ENOMEM;
- return -1;
- }
-
- i = 0;
- shellargv[i++] = shell;
- while(*shellargs != '\0') {
- shellargv[i++] = shellargs;
- while (*shellargs++ != '\0');
- }
-
- /* use the full pathname of the script instead of argv[0] */
- shellargv[i++] = (char *) _path;
- while (*++argv != NULL)
- shellargv[i++] = *argv;
- shellargv[i] = NULL;
-
- rv = _spawnve(mode, shell, shellargv, envp);
- (void)Mfree(shellargv);
- return rv;
- }
- }
-
- errno = ENOEXEC;
- return -1;
- }
-
- #endif /* HASH_BANG */
-
- int
- _spawnve(mode, _path, argv, envp)
- int mode;
- const char *_path;
- char *const *argv;
- char *const *envp;
- {
- char path[MAXPATHLEN];
- char cmd[TOS_ARGS + 1];
- size_t cmlen;
- size_t enlen = 0;
- size_t left, min_left;
- const char *p;
- char *s, *t;
- char *env;
- long rval;
- const char *pconv, *pc, *tmp;
- size_t len, cnt;
- int i, done;
- #ifdef HASH_BANG
- char *const *_envp;
- char *const *_argv;
- #endif
-
- if (mode != P_WAIT && mode != P_OVERLAY && mode != P_NOWAIT) {
- errno = EINVAL;
- return -1;
- }
- (void)_unx2dos(_path, path); /* convert filename, if necessary */
- if (!envp)
- envp = environ;
-
- #ifdef HASH_BANG
- _envp = envp;
- _argv = argv;
- #endif
-
- /* try to find PCONVERT environment variable */
- for (i = 0; (pconv = envp[i]) != 0; i++) {
- if (! strncmp(pconv, "PCONVERT=", 9)) {
- pconv += 9;
- break;
- }
- }
-
- /* count up space needed for environment */
- for(cmlen = 0; argv[cmlen]; cmlen++)
- enlen += strlen(argv[cmlen]) + 1;
- enlen += 64; /* filler for stuff like ARGV= and zeros,
- * minibuffer for empty param index conversion
- */
- min_left = enlen;
- for(cmlen = 0; envp[cmlen]; cmlen++)
- enlen += strlen(envp[cmlen]) + 1;
- enlen += 1024; /* buffer for _unx2dos */
-
- try_again:
- if ((env = (char *)Malloc((long)enlen)) == NULL) {
- errno = ENOMEM;
- return -1;
- }
- left = enlen;
- s = env;
-
- while ((p = *envp) != 0) {
- /*
- * NOTE: in main.c, we converted environment variables which contain paths into
- * POSIX form. Here, we convert back into gulam form. Note that the
- * new variable can be longer than the old, so space _is_ a problem.
- */
- done = 0;
-
- if (pconv) {
-
- pc = pconv;
- while (*pc) {
-
- tmp = pc; len = 0;
- while (*tmp && *tmp != ',') {
- tmp++; len++;
- }
-
- if (! strncmp(p, pc, len) && p[len] == '=') {
- len++;
- tmp = p + len; /* tmp now after '=' */
- cnt = 1;
- while (*tmp) { /* count words */
- if (*tmp == ':')
- cnt++;
- tmp++;
- }
-
- /* cnt * 2 is maximum enlargement when calling
- * _path_unx2dos. Make this agree with _unx2dos
- * in unx2dos.c
- */
- if (left - (strlen(p) + cnt * 2 + 1) < min_left)
- goto need_more_core;
-
- strncpy(s, p, len);
- _path_unx2dos(p + len, s + len);
-
- while (*s) {
- s++; left--;
- }
- s++; left--;
-
- done = 1;
- break;
- }
-
- if (! *tmp) break;
- pc = tmp + 1;
- }
- }
- else if (!strncmp(p, "PATH=", 5)) {
- strncpy(s, p, 5); s += 5; p += 5; left -= 5;
-
- tmp = p; /* tmp now after '=' */
- cnt = 1;
- while (*tmp) { /* count words */
- if (*tmp == ':')
- cnt++;
- tmp++;
- }
-
- /* cnt * 2 is maximum enlargement when calling
- * _path_unx2dos. Make this agree with _unx2dos
- * in unx2dos.c
- */
- if (left - (strlen(p) + cnt * 2 + 1) < min_left)
- goto need_more_core;
-
- _path_unx2dos(p, s);
-
- while (*s) {
- s++; left--;
- }
- s++; left--;
-
- done = 1;
- }
-
- if (! done) {
- /* copy variable without any conversion */
- while (*p) {
- *s++ = *p++;
- if (--left <= min_left) {
- need_more_core:
- /* oh dear, we don't have enough core...
- * so we Mfree what we already have, and try again with
- * some more space.
- */
- Mfree(env);
- enlen += 1024;
- goto try_again;
- }
- }
- *s++ = 0;
- left--;
- }
-
- envp++;
- }
-
- strcpy(s, "ARGV=");
- s += 6; /* s+=sizeof("ARGV=") */
-
- if (argv && *argv) {
- unsigned long null_params = 0;
- int digits, i;
- unsigned long idx, val;
- char *const *ap;
-
- /* communicate empty arguments thru ARGV= value
- */
- for (ap = argv, idx = 0;
- *ap;
- ap++, idx++)
- {
- if (! **ap) {
- /* empty argument found
- */
- if (! null_params) {
- strcpy(s-1, "NULL:");
- s += 4; /* s now points after "NULL:" */
- left -= 6;
- } else {
- *s++ = ',' ;
- }
- null_params++;
-
- /* convert index of zero param to ascii
- */
- if (idx == 0) {
- *s++ = '0';
- digits = 1;
- } else {
- digits = 0;
- val = idx;
- while (val) {
- for (i = digits; i > 0; i--)
- s[i] = s[i - 1];
- *s = "0123456789"[val % 10];
- val /= 10;
- digits++;
- }
- s += digits;
- }
-
- left -= digits + 2; /* 2 = sizeof( ',' in NULL:
- * list + ' ' we put in place
- * of empty params
- */
- if (left < min_left)
- goto need_more_core;
- }
- }
-
- if (null_params) {
- *s++ = 0; /* finish "NULL:" list */
- }
-
- /* copy argv[0] first (because it doesn't go into
- * the command line)
- */
- p = *argv;
- if (! *p) { /* if empty argument */
- *s++ = ' '; /* replace by space */
- } else {
- do {
- *s++ = *p++;
- } while (*p);
- }
- *s++ = '\0';
- }
-
- bzero(t = cmd, sizeof(cmd));
-
- /* s points at the environment's copy of the args */
- /* t points at the command line copy to be put in the basepage */
-
- cmlen = 0;
- if (argv && *argv) {
- t++;
- while (*++argv) {
- p = *argv;
- if (! *p) { /* if empty argument */
- *s++ = ' '; /* replace by space */
- /* write '' in TOS cmdlin
- */
- if (cmlen < TOS_ARGS) {
- *t++ = '\''; cmlen++;
- }
- if (cmlen < TOS_ARGS) {
- *t++ = '\''; cmlen++;
- }
- } else {
- do {
- if (cmlen < TOS_ARGS) {
- *t++ = *p; cmlen++;
- }
- *s++ = *p++;
- } while (*p);
- }
- if (cmlen < TOS_ARGS && *(argv+1)) {
- *t++ = ' '; cmlen++;
- }
- *s++ = '\0';
- }
- /* *cmd = (char) cmlen; NOT ANY MORE */
- }
-
- /* tie off environment */
- *s++ = '\0';
- *s = '\0';
-
- /* signal Extended Argument Passing */
- *cmd = 0x7f;
-
- /* MiNT and MicroRTX support background processes with Pexec(100,...) */
- /* MiNT supports overlays with Pexec(200,...) */
-
- if (mode == P_NOWAIT) cmlen = 100;
- else if (mode == P_OVERLAY && __mint) cmlen = 200;
- else cmlen = 0;
-
- rval = Pexec((int)cmlen, path, cmd, env);
-
- if (rval < 0)
- {
- #ifdef HASH_BANG
- if (rval == -ENOEXEC) {
- char *umode;
-
- /* try to find UNIXMODE in environment */
- if ((umode = getenv("UNIXMODE")) != NULL &&
- strchr(umode, 's') != NULL ) {
- (void)Mfree(env);
- return interpret_script(mode, path, _path, _argv, _envp);
- }
- }
- #endif
- errno = (int) -rval;
- rval = -1;
- }
- else if (mode == P_OVERLAY)
- /* note that we get here only if MiNT is not active! */
- _exit((int)rval);
- (void)Mfree(env);
- return (int) rval;
- }
-
- #ifdef TEST
- int
- main (int argc, char **argv, char **envp)
- {
- if (argc == 2) {
- if (spawnve(P_WAIT, argv[1], &argv[1], envp) < 0) {
- perror("spawn failed");
- return errno;
- }
- return 0;
- }
- return 1;
- }
- #endif
-